package cn.liwq.study.demo.netty.lab.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;

import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadLocalRandom;

/**
 * @author lweiqiang
 * @date 2021/4/26
 */
public class NettyChatServer {
    public static void main(String[] args) {
        // 1.声明线程池
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        // 2.引导类
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        // 3.设置线程池
        serverBootstrap
                .group(bossGroup, workerGroup)
                // 4.指定IO 类型 = ServerSocketChannel类型
                .channel(NioServerSocketChannel.class)
                // 5.设置参数
                .option(ChannelOption.SO_BACKLOG, 10)
                // 6.设置ServerSocketChannel对应的 handler
                .handler(new LoggingHandler(LogLevel.DEBUG))
                // 7.设置SocketChannel对应的 handler
                .childHandler(
                        new ChannelInitializer<SocketChannel>() {
                            @Override
                            protected void initChannel(SocketChannel ch) throws Exception {
                                ch.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));
                                ch.pipeline().addLast(new ChatChannelHandler());
                            }
                        });
        try {
            // 8.绑定端口
            ChannelFuture channelFuture = serverBootstrap.bind(8007).sync();
            // 9.等待服务端关闭连接
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 10.释放连接池
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    private static class ChatChannelHandler extends SimpleChannelInboundHandler<ByteBuf> {
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            System.out.println("new connection active:" + ctx.channel());
            ChatHolder.join((NioSocketChannel) ctx.channel());
        }

        @Override
        protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
            byte[] bytes = new byte[msg.readableBytes()];
            msg.readBytes(bytes);
            String content = new String(bytes, StandardCharsets.UTF_8).replace("\r\n", "");
            System.out.println(content);
            if (content.equals("quit")) {
                ctx.channel().close();
            } else {
                ChatHolder.propagate((SocketChannel) ctx.channel(), content);
            }
        }

        @Override
        public void channelInactive(ChannelHandlerContext ctx) throws Exception {
            System.out.println("connection inactive:" + ctx.channel());
            ChatHolder.quit((SocketChannel) ctx.channel());
        }
    }

    private static class ChatHolder {
        private static final Map<SocketChannel, String> USER_MAP = new ConcurrentHashMap<>();

        public static void join(SocketChannel socketChannel) {
            String userId = "用户" + ThreadLocalRandom.current().nextInt(Integer.MAX_VALUE);
            for (SocketChannel channel : USER_MAP.keySet()) {
                send(channel, userId + "加入群聊\n\r");
            }
            USER_MAP.put(socketChannel, userId);
        }

        public static void quit(SocketChannel socketChannel) {
            String userId = USER_MAP.remove(socketChannel);
            for (SocketChannel channel : USER_MAP.keySet()) {
                send(channel, userId + "退出了群聊\n\r");
            }
        }

        public static void propagate(SocketChannel socketChannel, String msg) {
            String userId = USER_MAP.get(socketChannel);
            for (SocketChannel channel : USER_MAP.keySet()) {
                if (!channel.equals(socketChannel)) {
                    send(channel, userId + ":" + msg + "\n\r");
                }
            }
        }

        private static void send(SocketChannel socketChannel, String msg) {
            try {
                ByteBuf writeBuffer = Unpooled.buffer(1024);
                writeBuffer.writeBytes(msg.getBytes(StandardCharsets.UTF_8));
                socketChannel.writeAndFlush(writeBuffer);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
